package com.example.snakefinal;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.MotionEvent;

import java.util.ArrayList;

public class Snake {
    public Paint paint;

    public String name;
    // 移动速度走几个格子
    private int speed = Const.SNAKE_SPEED;
    // 生命值
    private int lives;
    // 蛇蛇的所有格子
    private final ArrayList<Point> segmentLocations;
    // 蛇蛇宽度
    // How big is the entire grid
    // 地图大小
    // Where is the centre of the screen
    // horizontally in pixels?
    private final int halfWayPoint;

    public Heading getHeading() {
        return heading;
    }

    public void setPaint(Paint paint) {
        this.paint = paint;
    }

    // For tracking movement Heading
    protected enum Heading {
        UP, RIGHT, DOWN, LEFT
    }

    // 蛇头
    private Heading heading = Heading.RIGHT;
    // 下一帧蛇头朝向
    private Heading nextHeading = Heading.RIGHT;

    // A bitmap for each direction the head can face
    private Bitmap mBitmapHeadRight;
    private Bitmap mBitmapHeadLeft;
    private Bitmap mBitmapHeadUp;
    private Bitmap mBitmapHeadDown;

    // A bitmap for the body
    private Bitmap mBitmapBody;
    private Bitmap mBitmapBody2;
    protected final Point screenSize;

    private int score;
    private int highScore;

    public int skinResource;


    Snake(Context context, Point mr, int skinResource) {
        // Initialize our ArrayList
        segmentLocations = new ArrayList<>();
        // 蛇蛇实际宽度
        this.screenSize = mr;
        this.skinResource = skinResource;
        this.lives = 3;
        // Create and scale the bitmaps
        mBitmapHeadRight = BitmapFactory.decodeResource(context.getResources(), R.drawable.head);
        // Create 3 more versions of the head for different headings
        mBitmapHeadLeft = BitmapFactory.decodeResource(context.getResources(), R.drawable.head);
        mBitmapHeadUp = BitmapFactory.decodeResource(context.getResources(), R.drawable.head);
        mBitmapHeadDown = BitmapFactory.decodeResource(context.getResources(), R.drawable.head);

        // Modify the bitmaps to face the snake head
        // in the correct direction
        mBitmapHeadRight = Bitmap.createScaledBitmap(mBitmapHeadRight, Const.SIZE, Const.SIZE, false);

        // A matrix for scaling
        Matrix matrix = new Matrix();
        matrix.preScale(-1, 1);

        mBitmapHeadLeft = Bitmap.createBitmap(mBitmapHeadRight, 0, 0, Const.SIZE, Const.SIZE, matrix, true);

        // A matrix for rotating
        matrix.preRotate(-90);
        mBitmapHeadUp = Bitmap.createBitmap(mBitmapHeadRight, 0, 0, Const.SIZE, Const.SIZE, matrix, true);

        // Matrix operations are cumulative
        // so rotate by 180 to face down
        matrix.preRotate(180);
        mBitmapHeadDown = Bitmap.createBitmap(mBitmapHeadRight, 0, 0, Const.SIZE, Const.SIZE, matrix, true);

        // Create and scale the body
        mBitmapBody = BitmapFactory.decodeResource(context.getResources(), skinResource);
        mBitmapBody2 = BitmapFactory.decodeResource(context.getResources(), skinResource);

        mBitmapBody = Bitmap.createScaledBitmap(mBitmapBody, Const.SIZE, Const.SIZE, false);
        mBitmapBody2 = Bitmap.createScaledBitmap(mBitmapBody2, Const.SIZE, Const.SIZE, false);

        // The halfway point across the screen in pixels
        // Used to detect which side of screen was pressed
        halfWayPoint = mr.x / 2;
    }

    public void setHighScore(int highScore) {
        this.highScore = highScore;
    }

    public int getHighScore() {
        return highScore;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public int getScore() {
        return score;
    }

    // Get the snake ready for a new game
    void reset() {
        // 重置蛇头朝向
        heading = Heading.RIGHT;
        nextHeading = heading;
        lives = 3;
        // 清理蛇蛇身体
        segmentLocations.clear();
        // 初始化蛇蛇位置
        segmentLocations.add(new Point(screenSize.x / 2, screenSize.y / 2));
        if (score / 10 > highScore) {
            highScore = score / 10;
        }
    }

    public void increaseScore(int add) {
        this.score += add;
    }


    void move() {
        // Move the body
        // Start at the back and move it
        // to the position of the segment in front of it
        // 移动蛇身位置
        for (int i = segmentLocations.size() - 1; i > 0; i--) {
            segmentLocations.get(i).x = segmentLocations.get(i - 1).x;
            segmentLocations.get(i).y = segmentLocations.get(i - 1).y;
        }

        // Move the head in the appropriate heading
        // Get the existing head position
        Point headPoint = segmentLocations.get(0);

        // Move it appropriately
        switch (heading) {
            case UP:
                heading = nextHeading;
                headPoint.y -= speed;
                break;
            case RIGHT:
                heading = nextHeading;
                headPoint.x += speed;
                break;
            case DOWN:
                headPoint.y += speed;
                heading = nextHeading;
                break;
            case LEFT:
                headPoint.x -= speed;
                heading = nextHeading;
                break;
        }

    }

    boolean detectDeath() {
        // 生命是否为<=0
        if (this.lives <= 0) {
            return true;
        }
        Point headPoint = segmentLocations.get(0);
        // 是否撞墙
        if (headPoint.x <= 0
                || headPoint.x > Const.MAP_W - 100
                || headPoint.y <= 0
                || headPoint.y > Const.MAP_H - 100) {
            return true;
        }
        // 撞到自己
//        if (segmentLocations.size() > 9) {
//            for (int i = segmentLocations.size() - 1; i > 10; i--) {
//                // 与身体的某一节重合
//                if (checkBound(headPoint, segmentLocations.get(i))) {
//                    return true;
//                }
//            }
//        }
        return false;
    }

    boolean checkDinner(Food food) {
        Point head = segmentLocations.get(0);
        // 格子重复。
        return checkBound(head, food.getLocation());
    }

    public static boolean checkBound(Point point1, Point point2) {
        Rect rect1 = new Rect(point1.x, point1.y, point1.x + Const.SIZE, point1.y + Const.SIZE);
        Rect rect2 = new Rect(point2.x, point2.y, point2.x + Const.SIZE, point2.y + Const.SIZE);
        return Rect.intersects(rect1, rect2);
    }

    void draw(Canvas canvas) {
        int colorLength = 0;
        for (int i = 1; i < segmentLocations.size(); i++) {
            if (colorLength < 4) {
                canvas.drawBitmap(mBitmapBody, segmentLocations.get(i).x, segmentLocations.get(i).y, paint);
            } else {
                canvas.drawBitmap(mBitmapBody2, segmentLocations.get(i).x, segmentLocations.get(i).y, paint);
            }
            colorLength++;
            if (colorLength > 7) {
                colorLength = 0;
            }
        }

        // Don't run this code if ArrayList has nothing in it
        if (!segmentLocations.isEmpty()) {
            // All the code from this method goes here
            // Draw the head
            switch (heading) {
                case RIGHT:
                    canvas.drawBitmap(mBitmapHeadRight, segmentLocations.get(0).x, segmentLocations.get(0).y, paint);
                    break;
                case LEFT:
                    canvas.drawBitmap(mBitmapHeadLeft, segmentLocations.get(0).x, segmentLocations.get(0).y, paint);
                    break;
                case UP:
                    canvas.drawBitmap(mBitmapHeadUp, segmentLocations.get(0).x, segmentLocations.get(0).y, paint);
                    break;
                case DOWN:
                    canvas.drawBitmap(mBitmapHeadDown, segmentLocations.get(0).x, segmentLocations.get(0).y, paint);
                    break;
            }

        }
    }

    public synchronized void switchHeading(Heading heading) {
        nextHeading = heading;
    }


    // Handle changing direction
    void switchHeading(MotionEvent motionEvent) {
        // Is the tap on the right hand side?
        if (motionEvent.getX() >= halfWayPoint) {
            switch (heading) {
                // Rotate right
                case UP:
                    nextHeading = Heading.RIGHT;
                    break;
                case RIGHT:
                    nextHeading = Heading.DOWN;
                    break;
                case DOWN:
                    nextHeading = Heading.LEFT;
                    break;
                case LEFT:
                    nextHeading = Heading.UP;
                    break;
            }
        } else {
            // Rotate left
            switch (heading) {
                case UP:
                    nextHeading = Heading.LEFT;
                    break;
                case LEFT:
                    nextHeading = Heading.DOWN;
                    break;
                case DOWN:
                    nextHeading = Heading.RIGHT;
                    break;
                case RIGHT:
                    nextHeading = Heading.UP;
                    break;
            }
        }
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public int getSpeed() {
        return this.speed;
    }

    public int getLives() {
        return this.lives;
    }

    public void setLives(int add) {
        this.lives = add;
    }

    public ArrayList<Point> getSegmentLocations() {
        return this.segmentLocations;
    }
}